package com.ikun.admin.modules.auth.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ikun.admin.common.constant.CommonConstant;
import com.ikun.admin.modules.auth.constant.SecurityConstant;
import com.ikun.admin.modules.auth.eneity.SecurityUser;
import com.ikun.admin.modules.sys.entity.LogLogin;
import com.ikun.admin.modules.sys.entity.User;
import com.ikun.admin.modules.sys.iservice.IAclService;
import com.ikun.admin.modules.sys.service.LogLoginService;
import com.ikun.admin.modules.sys.service.UserService;
import com.ikun.admin.modules.sys.vo.RoleCommonVo;
import com.ikun.common.constant.RedisConstant;
import com.ikun.common.entity.SecurityUserInfo;
import com.ikun.common.exception.MyException;
import com.ikun.common.manager.RedisManager;
import com.ikun.common.utils.HttpContextUtils;
import com.ikun.starters.ip2region.IP2regionService;
import com.ikun.starters.jwt.impl.TokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName UserDetailService
 * @Description: 登录处理接口
 * @CreateDate: 2024/4/17 15:23
 */
@Component
public class LoginService implements UserDetailsService {


    private static final Logger log = LoggerFactory.getLogger(LoginService.class);
    @Autowired
    private UserService userService;

    @Autowired
    private RedisManager redisManager;

    @Autowired
    private LogLoginService logLoginService;

    @Resource
    private IP2regionService ip2regionService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private IAclService iAclService;

    @Autowired
    private AppAuthService appAuthService;

    @Autowired
    private TokenAboutRedisService tokenAboutRedisService;

    @Override
    public UserDetails loadUserByUsername(String userinfo) throws UsernameNotFoundException {
        log.info("用户登录用户名:{}", userinfo);
        SecurityUserInfo securityUserInfo = JSON.parseObject(userinfo, SecurityUserInfo.class);
        if (securityUserInfo.getLoginType() == 1) {
            //app登录
            return appAuthService.login(securityUserInfo);
        } else {
            //web登录
            User user = JSON.parseObject(userinfo, User.class);
            String username = user.getUsername();
            if (username == null)
                throw new MyException("用户名不能为空");
            //查询数据库
            return selectUser(username);
        }
    }

    /**
     * 查询数据库 并且封装数据
     *
     * @param username
     * @return
     */
    private UserDetails selectUser(String username) {

        User loginUser = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
        if (loginUser == null)
            throw new MyException("用户名有错");
        if (loginUser.getStatus() == CommonConstant.USER_STATUS_FREEZE)
            throw new MyException("当前用户在禁用期间");
        // 记录登录地址信息
        String ipAddr = recordLoginInfo(loginUser);
        // 获取上一次登录信息
        LogLogin logLogin = logLoginService.getOne(new QueryWrapper<LogLogin>().lambda().eq(LogLogin::getCreator, loginUser.getId()).last("limit 1"));
        // 封装权限和角色信息
        Long id = loginUser.getId();
        SecurityUserInfo securityUserInfo = new SecurityUserInfo();
        securityUserInfo.setId(id);
        securityUserInfo.setUsername(loginUser.getUsername());
        securityUserInfo.setPassword(loginUser.getPassword());
        securityUserInfo.setNickName(loginUser.getNickname());
        securityUserInfo.setHeadUrl(loginUser.getHeadUrl());
        securityUserInfo.setIpAddr(ipAddr);
        securityUserInfo.setLastLoginTime(Objects.isNull(logLogin) ? LocalDateTime.now() : logLogin.getCreateTime());
        //通过用户id查询用户的权限 用户id查询用户的角色
        List<RoleCommonVo> roleList = iAclService.selectRoleByUser(loginUser.getId());
        //获取权限
        List<String> permList = iAclService.selectMenuByUser(loginUser.getId());
        List<String> roleAndPermsList = new ArrayList<>();
        for (int i = 0; i < roleList.size(); i++) {
            roleAndPermsList.add(SecurityConstant.ROLE_PREFIX + roleList.get(i).getName());
        }
        roleAndPermsList.addAll(permList);
        //
        securityUserInfo.setRoleNameList(roleList.stream().map(item -> item.getName()).collect(Collectors.toList()));

        //生成token
        String token = tokenService.createToken(String.valueOf(id), username, null);
        securityUserInfo.setToken(token);
        tokenAboutRedisService.setAboutToken(RedisConstant.USER_PERM_PREFIX, token, roleAndPermsList, 12, TimeUnit.HOURS);
        tokenAboutRedisService.setAboutToken(RedisConstant.USER_ID_PREFIX, token, String.valueOf(id), 12, TimeUnit.HOURS);
        //记录登录状态
        tokenAboutRedisService.addSetAboutToken(RedisConstant.ONLINE_USER_SET, String.valueOf(id));

        // 记录登录id
        //记录登录用户信息
        //放到封装对象中去
        SecurityUser securityUser = new SecurityUser();
        securityUser.setUserInfo(securityUserInfo);
        securityUser.setRoleAndPerms(roleAndPermsList);
        return securityUser;
    }

    /**
     * 记录登录信息 并且返回登录地址
     *
     * @param user
     * @return
     * @throws IOException
     */
    private String recordLoginInfo(User user) {
        String publicIP = ip2regionService.getPublicIP();
        String ipAddr = ip2regionService.parseIp(publicIP);
        LogLogin logLogin = new LogLogin();
        String ip = HttpContextUtils.getIp();
        logLogin.setLoginIp(ip);
        logLogin.setCreator(user.getId());
        logLogin.setLoginAddr(ipAddr);
        logLoginService.save(logLogin);
        return ipAddr;
    }


}
